/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package de.tor.tribes.types;

import com.vaadin.terminal.ThemeResource;
import de.tor.tribes.reportserver.PersistenceImpl;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import javax.persistence.Transient;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;

/**
 *
 * @author Torridity
 */
@Entity
@Table(name = "Reports")
public class ZebraReport implements Serializable {

    public enum REPORT_COLOR {

        BLUE, GREEN, YELLOW, RED_YELLOW, RED_BLUE, RED, UNKNOWN;

        public ThemeResource getIcon() {
            switch (this) {
                case BLUE:
                    return new ThemeResource("../zebra/img/bullet_ball_blue.png");
                case GREEN:
                    return new ThemeResource("../zebra/img/bullet_ball_green.png");
                case YELLOW:
                    return new ThemeResource("../zebra/img/bullet_ball_yellow.png");
                case RED:
                    return new ThemeResource("../zebra/img/bullet_ball_red.png");
                case RED_YELLOW:
                    return new ThemeResource("../zebra/img/bullet_ball_red_yellow.png");
                case RED_BLUE:
                    return new ThemeResource("../zebra/img/bullet_ball_red_blue.png");
                default:
                    return new ThemeResource("../zebra/img/bullet_ball_grey.png");
            }
        }

        public String iconPath() {
            switch (this) {
                case BLUE:
                    return "../zebra/img/bullet_ball_blue.png";
                case GREEN:
                    return "../zebra/img/bullet_ball_green.png";
                case YELLOW:
                    return "../zebra/img/bullet_ball_yellow.png";
                case RED:
                    return "../zebra/img/bullet_ball_red.png";
                case RED_YELLOW:
                    return "../zebra/img/bullet_ball_red_yellow.png";
                case RED_BLUE:
                    return "../zebra/img/bullet_ball_red_blue.png";
                default:
                    return "../zebra/img/bullet_ball_grey.png";
            }
        }

        public String plainName() {
            switch (this) {
                case BLUE:
                    return "bullet_ball_blue";
                case GREEN:
                    return "bullet_ball_green";
                case YELLOW:
                    return "bullet_ball_yellow";
                case RED:
                    return "bullet_ball_red";
                case RED_YELLOW:
                    return "bullet_ball_red_yellow";
                case RED_BLUE:
                    return "bullet_ball_red_blue";
                default:
                    return "../zebra/img/bullet_ball_grey.png";
            }
        }

        @Override
        public String toString() {
            switch (this) {
                case BLUE:
                    return "Spähberichte";
                case GREEN:
                    return "Angriffe ohne Verluste";
                case YELLOW:
                    return "Angriffe mit Verlusten";
                case RED:
                    return "Keine Überlebenden";
                case RED_YELLOW:
                    return "Keine Überlebenden mit Gebäudebeschädigung";
                case RED_BLUE:
                    return "Keine Überlebenden mit Spähinformationen";
                default:
                    return "Unbekannt";
            }
        }
    }
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @OneToOne
    private ZebraUser owner;
    private String serverId;
    private String title;
    //header
    private boolean won;
    private boolean conquered;
    private long sent = 0;
    private double luck = 0.0;
    private short moral = 100;
    //attacker and defender
    private String attacker;
    private String source;
    private short xs = 0;
    private short ys = 0;
    private String defender;
    private String target;
    private short xt = 0;
    private short yt = 0;
    @OneToMany(mappedBy = "report", cascade = CascadeType.ALL)
    private Set<ZebraReportUnit> units;
    @OneToMany(mappedBy = "report", cascade = CascadeType.ALL)
    private Set<ZebraReportBuilding> buildings;
    //haul
    private int woodHaul = -1;
    private int clayHaul = -1;
    private int ironHaul = -1;
    private int maxHaul = -1;
    private int spiedWood = -1;
    private int spiedClay = -1;
    private int spiedIron = -1;
    //conquer
    private short acceptBefore = 100;
    private short acceptAfter = 100;
    @Enumerated
    private REPORT_COLOR color = REPORT_COLOR.UNKNOWN;
    @Transient
    private List<REPORT_COLOR> possibleReportType;

    public ZebraReport() {
        units = new HashSet<ZebraReportUnit>();
        buildings = new HashSet<ZebraReportBuilding>();
        possibleReportType = new LinkedList<REPORT_COLOR>();
        possibleReportType.addAll(Arrays.asList(REPORT_COLOR.BLUE, REPORT_COLOR.GREEN, REPORT_COLOR.YELLOW, REPORT_COLOR.RED, REPORT_COLOR.RED_BLUE, REPORT_COLOR.RED_YELLOW));
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Long getId() {
        return id;
    }

    public String getSendTime() {
        return new SimpleDateFormat("dd.MM.yy HH:mm:ss").format(new Date(sent));
    }

    /**
     * @return the serverId
     */
    public String getServerId() {
        return serverId;
    }

    /**
     * @param serverId the serverId to set
     */
    public void setServerId(String serverId) {
        this.serverId = serverId;
    }

    public void isNot(REPORT_COLOR... pColor) {
        possibleReportType.removeAll(Arrays.asList(pColor));
        if (possibleReportType.size() == 1) {
            //only one left...this should be the color
            color = possibleReportType.get(0);
        } else if (possibleReportType.isEmpty()) {
            //all removed ... bug!
            color = REPORT_COLOR.UNKNOWN;
        }
        //  System.out.println(possibleReportType);
    }

    /**
     * @return the color
     */
    public REPORT_COLOR getColor() {
        return color;
    }

    /**
     * @param color the color to set
     */
    public void setColor(REPORT_COLOR color) {
        this.color = color;
    }

    /**
     * @return the owner
     */
    public ZebraUser getOwner() {
        return owner;
    }

    /**
     * @param owner the owner to set
     */
    public void setOwner(ZebraUser owner) {
        this.owner = owner;
    }

    /**
     * @return the won
     */
    public boolean isWon() {
        return won;
    }

    /**
     * @param won the won to set
     */
    public void setWon(boolean won) {
        this.won = won;
    }

    /**
     * @return the conquered
     */
    public boolean isConquered() {
        return conquered;
    }

    /**
     * @param conquered the conquered to set
     */
    public void setConquered(boolean conquered) {
        this.conquered = conquered;
    }

    /**
     * @return the sent
     */
    public long getSent() {
        return sent;
    }

    /**
     * @param sent the sent to set
     */
    public void setSent(long sent) {
        this.sent = sent;
    }

    /**
     * @return the luck
     */
    public double getLuck() {
        return luck;
    }

    /**
     * @param luck the luck to set
     */
    public void setLuck(double luck) {
        this.luck = luck;
    }

    /**
     * @return the moral
     */
    public short getMoral() {
        return moral;
    }

    /**
     * @param moral the moral to set
     */
    public void setMoral(short moral) {
        this.moral = moral;
    }

    /**
     * @return the attacker
     */
    public String getAttacker() {
        if (attacker == null) {
            return "---";
        }
        return attacker;
    }

    /**
     * @param attacker the attacker to set
     */
    public void setAttacker(String attacker) {
        this.attacker = attacker;
    }

    /**
     * @return the source
     */
    public String getSource() {
        if (source == null) {
            return "---";
        }
        return source;
    }

    /**
     * @param source the source to set
     */
    public void setSource(String source) {
        this.source = source;
    }

    /**
     * @return the xs
     */
    public short getXs() {
        return xs;
    }

    /**
     * @param xs the xs to set
     */
    public void setXs(short xs) {
        this.xs = xs;
    }

    /**
     * @return the ys
     */
    public short getYs() {
        return ys;
    }

    /**
     * @param ys the ys to set
     */
    public void setYs(short ys) {
        this.ys = ys;
    }

    /**
     * @return the defender
     */
    public String getDefender() {
        if (defender == null) {
            return "---";
        }
        return defender;
    }

    /**
     * @param defender the defender to set
     */
    public void setDefender(String defender) {
        this.defender = defender;
    }

    /**
     * @return the target
     */
    public String getTarget() {
        if (target == null) {
            return "---";
        }
        return target;
    }

    /**
     * @param target the target to set
     */
    public void setTarget(String target) {
        this.target = target;
    }

    /**
     * @return the xt
     */
    public int getXt() {
        return xt;
    }

    /**
     * @param xt the xt to set
     */
    public void setXt(short xt) {
        this.xt = xt;
    }

    /**
     * @return the yt
     */
    public short getYt() {
        return yt;
    }

    /**
     * @param yt the yt to set
     */
    public void setYt(short yt) {
        this.yt = yt;
    }

    /**
     * @return the attackers
     */
    public Set<ZebraReportUnit> getUnits() {
        return units;
    }

    public Collection<ZebraReportUnit> getAttackers() {
        return CollectionUtils.select(units, new Predicate() {
            @Override
            public boolean evaluate(Object o) {
                return ((ZebraReportUnit) o).getType().equals(ZebraReportUnit.TYPE.ATTACKER);
            }
        });
    }

    public Collection<ZebraReportUnit> getDefenders() {
        return CollectionUtils.select(units, new Predicate() {
            @Override
            public boolean evaluate(Object o) {
                return ((ZebraReportUnit) o).getType().equals(ZebraReportUnit.TYPE.DEFENDER);
            }
        });
    }

    public Collection<ZebraReportUnit> getLostAttackers() {
        return CollectionUtils.select(units, new Predicate() {
            @Override
            public boolean evaluate(Object o) {
                return ((ZebraReportUnit) o).getType().equals(ZebraReportUnit.TYPE.LOST_ATTACKERS);
            }
        });
    }

    public Collection<ZebraReportUnit> getLostDefenders() {
        return CollectionUtils.select(units, new Predicate() {
            @Override
            public boolean evaluate(Object o) {
                return ((ZebraReportUnit) o).getType().equals(ZebraReportUnit.TYPE.LOST_DEFENDERS);
            }
        });
    }

    public Collection<ZebraReportUnit> getTroopsOutside() {
        return CollectionUtils.select(units, new Predicate() {
            @Override
            public boolean evaluate(Object o) {
                return ((ZebraReportUnit) o).getType().equals(ZebraReportUnit.TYPE.OUTSIDE);
            }
        });
    }

    public Collection<ZebraReportUnit> getTroopsOnTheWay() {
        return CollectionUtils.select(units, new Predicate() {
            @Override
            public boolean evaluate(Object o) {
                return ((ZebraReportUnit) o).getType().equals(ZebraReportUnit.TYPE.ON_THE_WAY);
            }
        });
    }

    public boolean getLostEverything() {
        return getDefenders().isEmpty();
    }

    public boolean wasSpied() {
        return spiedWood >= 0 || spiedClay >= 0 || spiedIron >= 0 || !buildings.isEmpty();
    }

    public boolean hasSpiedResources() {
        return spiedWood >= 0 || spiedClay >= 0 || spiedIron >= 0;
    }

    public boolean hasMiscInformation() {
        return woodHaul >= 0 || clayHaul >= 0 || ironHaul >= 0 || (acceptBefore != acceptAfter) || getWallDamage() != null || getBuildingDamage() != null;
    }

    public boolean hasHaulInformation() {
        return woodHaul >= 0 || clayHaul >= 0 || ironHaul >= 0;
    }

    /**
     * @param attackers the attackers to set
     */
    public void setUnits(Set<ZebraReportUnit> units) {
        this.units = units;
    }

    public Set<ZebraReportBuilding> getBuildings() {
        return buildings;
    }

    public ZebraReportBuilding[] getBuildings(boolean pSorted) {
        ZebraReportBuilding[] result = buildings.toArray(new ZebraReportBuilding[buildings.size()]);
        if (pSorted) {
            Arrays.sort(result, new Comparator<ZebraReportBuilding>() {
                public int compare(ZebraReportBuilding o1, ZebraReportBuilding o2) {
                    return Long.valueOf(o1.getBuilding().getId()).compareTo(Long.valueOf(o2.getBuilding().getId()));
                }
            });
        }
        return result;
    }

    public void setBuildings(Set<ZebraReportBuilding> buildings) {
        this.buildings = buildings;
    }

    public void addSpyedBuilding(DSBuilding pBuilding, int pLevel) {
        if (pBuilding == null) {
            throw new IllegalArgumentException("Argument pBuilding must not be null");
        }
        ZebraReportBuilding building = new ZebraReportBuilding();
        building.setLevelBefore(pLevel);
        building.setLevelAfter(-1);
        building.setBuilding(pBuilding);
        building.setReport(this);
        buildings.add(building);
    }

    public void addWallDestruction(int pLevelBefore, int pLevelAfter, PersistenceImpl pPersistence) {
        ZebraReportBuilding b = findBuildingByName("wall");

        if (b == null) {
            b = new ZebraReportBuilding();
            b.setBuilding(pPersistence.getBuildingByPlainName("wall"));
            b.setReport(this);
            buildings.add(b);
        }
        b.setLevelBefore(pLevelBefore);
        b.setLevelAfter(pLevelAfter);
    }

    public void addCataDestruction(final DSBuilding pBuilding, int pLevelBefore, int pLevelAfter) {
        if (pBuilding == null) {
            throw new IllegalArgumentException("Argument pBuilding must not be null");
        }
        ZebraReportBuilding b = findBuildingByName(pBuilding.getPlainName());

        if (b == null) {
            b = new ZebraReportBuilding();
            b.setBuilding(pBuilding);
            b.setReport(this);
            buildings.add(b);
        }
        b.setLevelBefore(pLevelBefore);
        b.setLevelAfter(pLevelAfter);
    }

    private ZebraReportBuilding findBuildingByName(final String pName) {
        return (ZebraReportBuilding) CollectionUtils.find(buildings, new Predicate() {
            public boolean evaluate(Object o) {
                ZebraReportBuilding b = (ZebraReportBuilding) o;
                return b.getBuilding().getPlainName().equals(pName);
            }
        });
    }

    public ZebraReportUnit findUnitByPlainName(final String pName, final ZebraReportUnit.TYPE pType) {
        return (ZebraReportUnit) CollectionUtils.find(units, new Predicate() {
            public boolean evaluate(Object o) {
                ZebraReportUnit u = (ZebraReportUnit) o;
                return u.getUnit().getPlainName().equals(pName) && u.getType().equals(pType);
            }
        });
    }

    public void addAttackerUnit(DSUnit pUnit, int pAmount) {
        if (pUnit == null) {
            throw new IllegalArgumentException("Argument pUnit must not be null");
        }
        ZebraReportUnit r = findUnitByPlainName(pUnit.getPlainName(), ZebraReportUnit.TYPE.ATTACKER);
        if (r == null) {
            r = new ZebraReportUnit();
            r.setReport(this);
            r.setUnit(pUnit);
            r.setType(ZebraReportUnit.TYPE.ATTACKER);
            units.add(r);
            if (!pUnit.getPlainName().equals("spy")) {
                isNot(REPORT_COLOR.BLUE);
            }
        }
        r.setAmount(pAmount);
    }

    public void addDefenderUnit(DSUnit pUnit, int pAmount) {
        ZebraReportUnit r = findUnitByPlainName(pUnit.getPlainName(), ZebraReportUnit.TYPE.DEFENDER);
        if (r == null) {
            r = new ZebraReportUnit();
            r.setReport(this);
            r.setUnit(pUnit);
            r.setType(ZebraReportUnit.TYPE.DEFENDER);
            units.add(r);
        }
        r.setAmount(pAmount);
    }

    public void addLostDefenderUnit(DSUnit pUnit, int pAmount) {
        ZebraReportUnit r = findUnitByPlainName(pUnit.getPlainName(), ZebraReportUnit.TYPE.LOST_DEFENDERS);
        if (r == null) {
            r = new ZebraReportUnit();
            r.setReport(this);
            r.setUnit(pUnit);
            r.setType(ZebraReportUnit.TYPE.LOST_DEFENDERS);
            units.add(r);
        }
        r.setAmount(pAmount);
    }

    public void addLostAttackerUnit(DSUnit pUnit, int pAmount) {
        ZebraReportUnit r = findUnitByPlainName(pUnit.getPlainName(), ZebraReportUnit.TYPE.LOST_ATTACKERS);
        if (r == null) {
            r = new ZebraReportUnit();
            r.setReport(this);
            r.setUnit(pUnit);
            r.setType(ZebraReportUnit.TYPE.LOST_ATTACKERS);
            units.add(r);
        }
        r.setAmount(pAmount);
    }

    public void addTroopsOutside(DSUnit pUnit, int pAmount) {
        ZebraReportUnit r = findUnitByPlainName(pUnit.getPlainName(), ZebraReportUnit.TYPE.OUTSIDE);
        if (r == null) {
            r = new ZebraReportUnit();
            r.setReport(this);
            r.setUnit(pUnit);
            r.setType(ZebraReportUnit.TYPE.OUTSIDE);
            units.add(r);
        }
        r.setAmount(r.getAmount() + pAmount);
    }

    public void addTroopsOnTheWay(DSUnit pUnit, int pAmount) {
        ZebraReportUnit r = findUnitByPlainName(pUnit.getPlainName(), ZebraReportUnit.TYPE.ON_THE_WAY);
        if (r == null) {
            r = new ZebraReportUnit();
            r.setReport(this);
            r.setUnit(pUnit);
            r.setType(ZebraReportUnit.TYPE.OUTSIDE);
            units.add(r);
        }
        r.setAmount(r.getAmount() + pAmount);
    }

    /**
     * @return the woodHaul
     */
    public int getWoodHaul() {
        return woodHaul;
    }

    /**
     * @param woodHaul the woodHaul to set
     */
    public void setWoodHaul(int woodHaul) {
        this.woodHaul = woodHaul;
    }

    /**
     * @return the clayHaul
     */
    public int getClayHaul() {
        return clayHaul;
    }

    /**
     * @param clayHaul the clayHaul to set
     */
    public void setClayHaul(int clayHaul) {
        this.clayHaul = clayHaul;
    }

    /**
     * @return the ironHaul
     */
    public int getIronHaul() {
        return ironHaul;
    }

    /**
     * @param ironHaul the ironHaul to set
     */
    public void setIronHaul(int ironHaul) {
        this.ironHaul = ironHaul;
    }

    /**
     * @return the maxHaul
     */
    public int getMaxHaul() {
        return maxHaul;
    }

    public ZebraReportBuilding getWallDamage() {
        return (ZebraReportBuilding) CollectionUtils.find(buildings, new Predicate() {
            public boolean evaluate(Object o) {
                ZebraReportBuilding b = (ZebraReportBuilding) o;
                return b.getBuilding().getPlainName().equals("wall")
                        && b.getLevelBefore() != b.getLevelAfter()
                        && b.getLevelAfter() >= 0;
            }
        });
    }

    public ZebraReportBuilding getBuildingDamage() {
        return (ZebraReportBuilding) CollectionUtils.find(buildings, new Predicate() {
            public boolean evaluate(Object o) {
                ZebraReportBuilding b = (ZebraReportBuilding) o;
                return !b.getBuilding().getPlainName().equals("wall")
                        && b.getLevelBefore() != b.getLevelAfter()
                        && b.getLevelAfter() >= 0;
            }
        });
    }

    public boolean wasSnobAttack() {
        return getAcceptBefore() != getAcceptAfter();
    }

    /**
     * @param maxHaul the maxHaul to set
     */
    public void setMaxHaul(int maxHaul) {
        this.maxHaul = maxHaul;
    }

    /**
     * @return the spiedWood
     */
    public int getSpiedWood() {
        return spiedWood;
    }

    /**
     * @param spiedWood the spiedWood to set
     */
    public void setSpiedWood(int spiedWood) {
        this.spiedWood = spiedWood;
    }

    /**
     * @return the spiedClay
     */
    public int getSpiedClay() {
        return spiedClay;
    }

    /**
     * @param spiedClay the spiedClay to set
     */
    public void setSpiedClay(int spiedClay) {
        this.spiedClay = spiedClay;
    }

    /**
     * @return the spiedIron
     */
    public int getSpiedIron() {
        return spiedIron;
    }

    /**
     * @param spiedIron the spiedIron to set
     */
    public void setSpiedIron(int spiedIron) {
        this.spiedIron = spiedIron;
    }

    /**
     * @return the acceptBefore
     */
    public short getAcceptBefore() {
        return acceptBefore;
    }

    /**
     * @param acceptBefore the acceptBefore to set
     */
    public void setAcceptBefore(short acceptBefore) {
        this.acceptBefore = acceptBefore;
    }

    /**
     * @return the acceptAfter
     */
    public short getAcceptAfter() {
        return acceptAfter;
    }

    /**
     * @param acceptAfter the acceptAfter to set
     */
    public void setAcceptAfter(short acceptAfter) {
        this.acceptAfter = acceptAfter;
    }

    public String getTitle() {
        if (title == null) {
            return getAttacker() + " greift " + getTarget() + " an";
        }
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public void delete(PersistenceImpl pPersistence) {
        List<ZebraReportAccessGrant> grants = pPersistence.getReportsAccessGrants(this);
        for (ZebraReportAccessGrant grant : grants.toArray(new ZebraReportAccessGrant[]{})) {
            grant.delete(pPersistence);
        }

        pPersistence.remove(this);
    }
}
